/**
 * @file Picture.cpp
 *
 * @date 20/12/2011
 * @author Tales 
 *
 */

#include "Picture.hpp"
#include "Command.hpp"
#include "PictureState.hpp"
#include "Layer.hpp"
#include "../common/Error.hpp"
#include "../Graphics.hpp"

namespace yep
{

Picture::Picture(int w, int h, int bpp) :
		first(w, h, bpp)
{
	current = new PictureState(first);
}

Picture::Picture(SDL_RWops *file, bool importFromBmp)
{
	if (importFromBmp)
	{
		SDL_Surface *imported = SDL_LoadBMP_RW(file, 0);
		if (imported->format->BitsPerPixel < 32)
			SDL_SetColorKey(imported, SDL_SRCCOLORKEY, SDL_MapRGB(imported->format, 255, 255, 255));
		first.setBpp(imported->format->BitsPerPixel);
		first.setW(imported->w);
		first.setH(imported->h);
		first.addLayer(imported);
	}
	else
	{
		Uint16 nLayers = SDL_ReadLE16(file);
		SDL_Surface *layer = SDL_LoadBMP_RW(file, 0);
		if (layer->format->BitsPerPixel < 32)
			SDL_SetColorKey(layer, SDL_SRCCOLORKEY, SDL_MapRGB(layer->format, 255, 255, 255));
		first.setBpp(layer->format->BitsPerPixel);
		first.setW(layer->w);
		first.setH(layer->h);
		first.addLayer(layer);
		for (Uint16 i = 1; i < nLayers; ++i)
		{
			layer = SDL_LoadBMP_RW(file, 0);
			if (layer->format->BitsPerPixel < 32)
				SDL_SetColorKey(layer, SDL_SRCCOLORKEY, SDL_MapRGB(layer->format, 255, 255, 255));
			first.addLayer(layer);
		}
	}
	current = new PictureState(first);
}

Picture::~Picture()
{
	delete current;
}

void Picture::save(SDL_RWops *file, bool exportToBmp)
{
	if (exportToBmp)
	{
		SDL_Surface *temp = SDL_CreateRGBSurface(SDL_SWSURFACE, current->getW(), current->getH(), 24, 0xff0000,
				0x00ff00, 0x0000ff, 0);
		SDL_FillRect(temp, NULL, 0xffffff);
		int max = current->getLayerCount();
		Graphics g(temp, true);
		for (int i = 0; i < max; ++i)
		{
			g.blit(current->getLayer(i).getSurface(), make_pair(0, 0));
		}
		SDL_SaveBMP_RW(temp, file, 0);
	}
	else
	{
		Uint16 nLayers = (Uint16) getLayerCount();
		SDL_WriteLE16(file, nLayers);
		for (Uint16 i = 0; i < nLayers; ++i)
		{
			SDL_SaveBMP_RW(current->getLayer(i).getSurface(), file, 0);
		}
	}
}

void Picture::addCommand(Command *command)
{
	if (canRedo())
	{
		commands.erase(currentCommand, commands.end());
	}
	commands.push_back(command);
	currentCommand = commands.end();
	command->execute(*current);
}

void Picture::render(SDL_Surface *dst)
{
	for (size_t i = 0; i < current->getLayerCount(); ++i)
	{
		SDL_BlitSurface(current->getLayer(i).getSurface(), NULL, dst, NULL);
	}
}

bool Picture::canUndo()
{
	return currentCommand != commands.begin() && commands.size() > 0;
}

void Picture::undo()
{
	if (canUndo())
	{
		delete current;
		current = new PictureState(first);
		--currentCommand;
		for (auto it = commands.begin(); it != currentCommand; ++it)
		{
			Command &com = **it;
			com.execute(*current);
		}
	}
}

void Picture::revertAll()
{
	currentCommand = commands.begin();
	current = new PictureState(first);
}

bool Picture::canRedo()
{
	return commands.size() > 0 && currentCommand != commands.end();
}

void Picture::redo()
{
	if (canRedo())
	{
		(*currentCommand)->execute(*current);
		++currentCommand;
	}
}

SDL_Surface *Picture::getCopy(size_t layer)
{
	return Graphics(current->getLayer(layer).getSurface()).clone();
}

SDL_Surface *Picture::getCopy(size_t layer, const SDL_Rect & slice)
{
	return Graphics(current->getLayer(layer).getSurface()).clone(slice);
}

void Picture::doPermanently()
{
	first = *current;
	currentCommand = commands.erase(commands.begin(), currentCommand);
}

size_t Picture::getLayerCount()
{
	return current->getLayerCount();

}

int Picture::getW() const
{
	return current->getW();
}

int Picture::getH() const
{
	return current->getH();
}

} /* namespace yep */
